#include "gameClient.h"

GameClient* game;
bool keys[256];

void GameClient::createPlayer(ClientSideClient* clientSideClient)
{
	Ogre::Entity* NinjaEntity = mSceneMgr->createEntity("ninja.mesh");
	Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    node->attachObject(NinjaEntity);
	clientSideClient->myNode = node;
}
void GameClient::createServerPlayer(ClientSideClient* clientSideClient)
{
    Ogre::Entity* NinjaEntity = mSceneMgr->createEntity("ninja.mesh");
    Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    node->attachObject(NinjaEntity);
    clientSideClient->myServerNode = node;
}
//-------------------------------------------------------------------------------------
void GameClient::createScene(void)
{
    mSceneMgr->setAmbientLight(Ogre::ColourValue(0.75, 0.75, 0.75));

    Ogre::Light* pointLight = mSceneMgr->createLight("pointLight");
    pointLight->setType(Ogre::Light::LT_POINT);
    pointLight->setPosition(Ogre::Vector3(250, 150, 250));
    pointLight->setDiffuseColour(Ogre::ColourValue::White);
    pointLight->setSpecularColour(Ogre::ColourValue::White);
}
//-------------------------------------------------------------------------------------
bool GameClient::processUnbufferedInput(const Ogre::FrameEvent& evt)
{
    if (mKeyboard->isKeyDown(OIS::KC_I)) // Forward
    {
		keys[VK_UP] = TRUE;
    }
	else
	{
        keys[VK_UP] = FALSE;
	}
    if (mKeyboard->isKeyDown(OIS::KC_K)) // Backward
    {
		keys[VK_DOWN] = TRUE;
    }
	else
	{
        keys[VK_DOWN] = FALSE;
	}
    if (mKeyboard->isKeyDown(OIS::KC_J)) // Left - yaw or strafe
    {
		keys[VK_LEFT] = TRUE;
    }
	else
	{
        keys[VK_LEFT] = FALSE;
	}
    if (mKeyboard->isKeyDown(OIS::KC_L)) // Right - yaw or strafe
    {
		keys[VK_RIGHT] = TRUE;
    }
	else
	{
        keys[VK_RIGHT] = FALSE;
	}

	if (mKeyboard->isKeyDown(OIS::KC_V)) // toggle serverPlayerMovement
	{
		mServerPlayerToggle = !mServerPlayerToggle;
	}

    return true;
}
//-------------------------------------------------------------------------------------
bool GameClient::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
    bool ret = BaseApplication::frameRenderingQueued(evt);

    if(!processUnbufferedInput(evt)) return false;

	if(game != NULL)
	{
		game->CheckKeys();
		game->RunNetwork(evt.timeSinceLastFrame * 1000);
		rendertime = evt.timeSinceLastFrame;
	}
    return ret;
}

GameClient::GameClient(const char* serverIP)
{
	mServerIP = serverIP;
 	networkClient	= new dreamClient;
 	localClient		= NULL;
 	memset(&inputClient, 0, sizeof(ClientSideClient));
 	frametime		= 0.0f;
 	rendertime		= 0.0f;
	tickIndex       = 0;
 	init			= false;
	mServerPlayerToggle = false;
 }

 GameClient::~GameClient()
 {
 	delete networkClient;
 }

 void GameClient::Shutdown(void)
 {
 	Disconnect();
 }

 ClientSideClient *GameClient::GetClientPointer(int index)
 {
 	for (int i = 0; i < clientSideClientVector.size(); i++)
 	{
		if(clientSideClientVector.at(i)->index == index)
 			return clientSideClientVector.at(i);
 	}
 	return NULL;
 }

void GameClient::CheckKeys(void)
{
	inputClient.command.key = 0;
 	if(keys[VK_ESCAPE])
 	{
 		Shutdown();
 		keys[VK_ESCAPE] = false;
 	}
 	if(keys[VK_DOWN])
 	{
 		inputClient.command.key |= KEY_DOWN;
 	}
 	if(keys[VK_UP])
 	{
 		inputClient.command.key |= KEY_UP;
 	}
 	if(keys[VK_LEFT])
 	{
 		inputClient.command.key |= KEY_LEFT;
 	}
 	if(keys[VK_RIGHT])
 	{
 		inputClient.command.key |= KEY_RIGHT;
 	}
 	inputClient.command.msec = (int) (frametime * 1000);
 }

 void GameClient::CalculateVelocity(ClientSideCommand *command, float frametime)
 {
 	float multiplier = 100.0f;

 	command->vel.x = 0.0f;
 	command->vel.y = 0.0f;

 	if(command->key & KEY_UP)
 	{
 		command->vel.y += multiplier * frametime;
 	}
 	if(command->key & KEY_DOWN)
 	{
 		command->vel.y += -multiplier * frametime;
 	}
 	if(command->key & KEY_LEFT)
 	{
 		command->vel.x += -multiplier * frametime;
 	}
 	if(command->key & KEY_RIGHT)
 	{
 		command->vel.x += multiplier * frametime;
 	}
 }

void GameClient::ProcessTick(ClientSideClient *client)
{
	bool flag;
	int j;
	int diffX;
	int diffY;
	int diffTime;
	int oldTime;
	int newTime;
	Vector3D prevDest;

	flag = false;

	// set index to current array spot
    j = client->command.curIndex;

	// get timestamp from previous tick
	oldTime = client->serverFrame[j].realtime;
	// get previous destination
	prevDest = client->command.curDest;

    // increment to next tick in array
	j = j + 1;

	// loop back to start of array when we reach the end
    if(j > 9)
		j = j - 10;

	// make sure we got a future tick and not an old one
	LogString("framenum %d", client->serverFrame[j].framenum);
	LogString("curTick %d", client->command.curTick);
	if(client->serverFrame[j].framenum > client->command.curTick) {
	   client->command.curDest = client->serverFrame[j].origin;
	   client->command.curTick = client->serverFrame[j].framenum;
	   newTime = client->serverFrame[j].realtime;
	   client->command.curIndex = j;
       LogString("flag");
	   flag = true;
	}


	// if we get a tick calculate new velocity
	if(flag) {
		diffX = client->command.curDest.x - prevDest.x;
	    diffY = client->command.curDest.y - prevDest.y;
		diffTime = newTime - oldTime;
        client->command.vel.x = diffX/diffTime;
		client->command.vel.y = diffY/diffTime;
	}
	else {
	   // reset buffer
		tickBuffer = 0;

		//keep same vel
		client->command.vel.x = 0.0;
		client->command.vel.y = 0.0;
	}
}

bool GameClient::ReachDestination(ClientSideClient *client)
{
   if(abs(client->myNode->getPosition().x - client->command.curDest.x) < 1.0 &&
	  abs(client->myNode->getPosition().y - client->command.curDest.y) < 1.0)
	  return true;
   else
	   return false;
}

void GameClient::interpolateTick(ClientSideClient *client)
{
    Ogre::Vector3 transVector = Ogre::Vector3::ZERO;

	transVector.x = client->command.vel.x;
	transVector.y = client->command.vel.y;

   // LogString("velX %f", client->command.vel.x);
	//LogString("velY %f", client->command.vel.y);
		
    client->myNode->translate(transVector * rendertime * 1000, Ogre::Node::TS_WORLD);

}

void GameClient::MoveRemotePlayers(void)
{
 	if(!localClient)
		return;

 	Ogre::Vector3 transVector = Ogre::Vector3::ZERO;
	for (int i = 0; i < clientSideClientVector.size(); i++)
 	{
		CalculateVelocity(&clientSideClientVector.at(i)->command, rendertime);

 		clientSideClientVector.at(i)->command.origin.x += clientSideClientVector.at(i)->command.vel.x;
 		clientSideClientVector.at(i)->command.origin.y += clientSideClientVector.at(i)->command.vel.y;

		transVector.x = clientSideClientVector.at(i)->command.vel.x;
		transVector.y = clientSideClientVector.at(i)->command.vel.y;

	    clientSideClientVector.at(i)->myNode->translate(transVector, Ogre::Node::TS_WORLD);
	}
 }

void GameClient::MoveServerPlayer(void)
{
	if(!localClient)
		return;
 
    Ogre::Vector3 transVector = Ogre::Vector3::ZERO;

	transVector.x = localClient->clientSideCommandVector.back()->origin.x;
	transVector.y = localClient->clientSideCommandVector.back()->origin.y;
        
    if (mServerPlayerToggle)
		localClient->myServerNode->setPosition(transVector);
}

void GameClient::MoveServerPlayerSmoothTest(void)
{
	if(!localClient)
		return;
 
    Ogre::Vector3 transVector = Ogre::Vector3::ZERO;

 	if(keys[VK_DOWN])
 	{
		transVector.y = -1;
 	}
 	if(keys[VK_UP])
 	{
		transVector.y = 1;
 	}
 	if(keys[VK_LEFT])
 	{
		transVector.x = -1;
 	}
 	if(keys[VK_RIGHT])
 	{
		transVector.x = 1;
 	}

	if (mServerPlayerToggle)
		localClient->myServerNode->translate(transVector,Ogre::Node::TS_WORLD);
}


void GameClient::StartConnection()
{
	int ret = networkClient->Initialise("", mServerIP, 30004);

	if(ret == DREAMSOCK_CLIENT_ERROR)
	{
		char text[64];
		sprintf(text, "Could not open client socket");
	}
	Connect();
}

void GameClient::ReadPackets(void)
{
	char data[1400];
	struct sockaddr address;

	int type;
	int ind;
	int local;
	int ret;

	char name[50];

	dreamMessage mes;
	mes.Init(data, sizeof(data));

	while(ret = networkClient->GetPacket(mes.data, &address))
	{
		mes.SetSize(ret);
		mes.BeginReading();

		type = mes.ReadByte();

		switch(type)
		{
		case DREAMSOCK_MES_ADDCLIENT:
			local	= mes.ReadByte();
			ind		= mes.ReadByte();
			strcpy(name, mes.ReadString());

			AddClient(local, ind, name);
			break;

		case DREAMSOCK_MES_REMOVECLIENT:
			ind = mes.ReadByte();

			LogString("Got removeclient %d message", ind);

			RemoveClient(ind);

			break;

		case USER_MES_FRAME:
			// Skip sequences
			mes.ReadShort();
			mes.ReadShort();

			for (int i = 0; i < clientSideClientVector.size(); i++)
			{
				ReadDeltaMoveCommand(&mes, clientSideClientVector.at(i));
			}

			break;

		case USER_MES_NONDELTAFRAME:
			// Skip sequences
			mes.ReadShort();
			mes.ReadShort();

			for (int i = 0; i < clientSideClientVector.size(); i++)
			{
				LogString("Reading NONDELTAFRAME for client %d", clientSideClientVector.at(i)->index);
				ReadMoveCommand(&mes, clientSideClientVector.at(i));
			}

			break;

		case USER_MES_SERVEREXIT:
			//MessageBox(NULL, "Server disconnected", "Info", MB_OK);
			Disconnect();
			break;

		}
	}
}

void GameClient::AddClient(int local, int ind, char *name)
{
	ClientSideClient* clientSideClient = new ClientSideClient();
	clientSideClientVector.push_back(clientSideClient);
	LogString("pushed clientSideClient into clientSideClientVector");
	createPlayer(clientSideClient);
	LogString("finished createPlayer");
	if(local)
	{
		LogString("App: Client: This one is local");
		localClient = clientSideClient;
		createServerPlayer(clientSideClient);
		SendRequestNonDeltaFrame();
	}
}

void GameClient::RemoveClient(int ind)
{
	for (int i = 0; i < clientSideClientVector.size(); i++)
	{
		if (clientSideClientVector.at(i)->index == ind)
		{
			clientSideClientVector.erase(clientSideClientVector.begin()+i);
			return;
		}
	}
}


void GameClient::SendCommand(void)
{
	if(networkClient->GetConnectionState() != DREAMSOCK_CONNECTED)
		return;

	dreamMessage message;
	char data[1400];

	int i = networkClient->GetOutgoingSequence() & (COMMAND_HISTORY_SIZE-1);

	message.Init(data, sizeof(data));
	message.WriteByte(USER_MES_FRAME);						// type
	message.AddSequences(networkClient);					// sequences

	// Build delta-compressed move command
	BuildDeltaMoveCommand(&message, &inputClient);

	// Send the packet
	networkClient->SendPacket(&message);

	// Store the command to the input client's history
	memcpy(&inputClient.frame[i], &inputClient.command, sizeof(ClientSideCommand));

	// Store the commands to the clients' history
	for (int i = 0; i < clientSideClientVector.size(); i++)
	{
		memcpy(&clientSideClientVector.at(i)->frame[i], &clientSideClientVector.at(i)->command, sizeof(ClientSideCommand));
	}
}

void GameClient::SendRequestNonDeltaFrame(void)
{
	char data[1400];
	dreamMessage message;
	message.Init(data, sizeof(data));

	message.WriteByte(USER_MES_NONDELTAFRAME);
	message.AddSequences(networkClient);

	networkClient->SendPacket(&message);
}

void GameClient::Connect(void)
{
	if(init)
	{
		LogString("ArmyWar already initialised");
		return;
	}

	LogString("GameClient::Connect");

	init = true;

	networkClient->SendConnect("myname");
}

void GameClient::Disconnect(void)
{
	if(!init)
		return;

	LogString("GameClient::Disconnect");

	init = false;
	localClient = NULL;
	memset(&inputClient, 0, sizeof(ClientSideClient));

	networkClient->SendDisconnect();
}

void GameClient::ReadMoveCommand(dreamMessage *mes, ClientSideClient *client)
{
	
	ClientSideCommand *clientSideCommand = new ClientSideCommand();
	client->clientSideCommandVector.push_back(clientSideCommand);

	// Key
	clientSideCommand->key				= mes->ReadByte();

	// Origin
	clientSideCommand->origin.x		= mes->ReadFloat();
	clientSideCommand->origin.y		= mes->ReadFloat();
	clientSideCommand->vel.x		= mes->ReadFloat();
	clientSideCommand->vel.y		= mes->ReadFloat();

	//Read realtime and ticknumber
	clientSideCommand->realtime = mes->ReadShort();
	clientSideCommand->framenum = mes->ReadLong();

	// Read time to run command
	clientSideCommand->msec = mes->ReadByte();

	}

void GameClient::ReadDeltaMoveCommand(dreamMessage *mes, ClientSideClient *client)
{
	ClientSideCommand *clientSideCommand = new ClientSideCommand();
	client->clientSideCommandVector.push_back(clientSideCommand);

	int processedFrame;
	int flags = 0;

	if(tickIndex > 9)
		tickIndex = 0;

	// Flags
	flags = mes->ReadByte();

	// Key
	if(flags & CMD_KEY)
	{
		clientSideCommand->key = mes->ReadByte();
		LogString("Client %d: Read key %d", client->index, clientSideCommand->key);
	}

	if(flags & CMD_ORIGIN)
	{
		processedFrame = mes->ReadByte();
	}

	// Origin
	if(flags & CMD_ORIGIN)
	{
		tickBuffer++;

		clientSideCommand->origin.x = mes->ReadFloat();
		clientSideCommand->origin.y = mes->ReadFloat();

		clientSideCommand->vel.x = mes->ReadFloat();
		clientSideCommand->vel.y = mes->ReadFloat();
	}

	//Read realtime and ticknumber
	clientSideCommand->realtime = mes->ReadShort();
	clientSideCommand->framenum = mes->ReadLong();

	// Read time to run command
	clientSideCommand->msec = mes->ReadByte();

	tickIndex++;
}

//-----------------------------------------------------------------------------
// Name: empty()
// Desc:
//-----------------------------------------------------------------------------
void GameClient::BuildDeltaMoveCommand(dreamMessage *mes, ClientSideClient *theClient)
{
	int flags = 0;
	int last = (networkClient->GetOutgoingSequence() - 1) & (COMMAND_HISTORY_SIZE-1);

	// Check what needs to be updated
	if(theClient->frame[last].key != theClient->command.key)
		flags |= CMD_KEY;

	// Add to the message
	// Flags
	mes->WriteByte(flags);

	// Key
	if(flags & CMD_KEY)
	{
		mes->WriteByte(theClient->command.key);
	}

	mes->WriteByte(theClient->command.msec);
}

//-----------------------------------------------------------------------------
// Name: empty()
// Desc:
//-----------------------------------------------------------------------------
void GameClient::RunNetwork(int msec)
{
	static int time = 0;
	time += msec;
    LogString("RunNetwork");
    if(tickBuffer == 6)
	{
		LogString("tickBuffer == 6");
		for (int i = 0; i < clientSideClientVector.size(); i++)
		{
			ProcessTick(clientSideClientVector.at(i)); 
		}
	}

	if(tickBuffer > 6)
	{
		LogString("tickBuffer > 6");
		for (int i = 0; i < clientSideClientVector.size(); i++)
		{
		   if(ReachDestination(clientSideClientVector.at(i)))
			   ProcessTick(clientSideClientVector.at(i)); 

		   interpolateTick(clientSideClientVector.at(i));
		}
	}
	MoveServerPlayer();
	//MoveRemotePlayers();

	// Framerate is too high
	if(time < (1000 / 60)) {
		return;
	}

	frametime = time / 1000.0f;
	time = 0;

	// Read packets from server, and send new commands
	ReadPackets();
	SendCommand();
}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
    int main(int argc, char *argv[])
#endif
    {

        //ClientSideBaseGame* mClientSideBaseGame;
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        game = new GameClient(strCmdLine);
#else
        game = new GameClient(argv[1]);
#endif

		//game = new GameClient;
	    game->StartConnection();

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		StartLogConsole();
#endif

        try {
            game->go();
        } catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
            std::cerr << "An exception has occured: " <<
                e.getFullDescription().c_str() << std::endl;
#endif
        }

        return 0;
    }

#ifdef __cplusplus
}
#endif